//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Windows;
using System.Windows.Media;
using OpenLS.Drawing;
using OpenLS.Drawing.UI;

namespace OpenLS.Spreadsheet.UI.Internals
{
    public class CellBordersControl : CellControl
    {
        private Dictionary<Pair<SpreadsheetColor, BorderStyle>, Pen> _pens =
            new Dictionary<Pair<SpreadsheetColor, BorderStyle>, Pen>();

        private ColorScheme _previousScheme;
        private CellsWindowView _view;

        public CellBordersControl()
        {
            Unloaded += CellBordersControlUnloaded;
        }

        private CellsWindowView View
        {
            get
            {
                Contract.Ensures(Contract.Result<CellsWindowView>() != null);
                if (_view == null)
                    _view = new CellsWindowView(((WorksheetPane) WorksheetPane).Window,
                                                c =>
                                                c.Borders != null);
                return _view;
            }
        }

        internal override void OnWorksheetPaneChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var oldFragment = (IWorksheetFragment) e.OldValue;
            var newFragment = (IWorksheetFragment) e.NewValue;
            if (oldFragment != null)
                oldFragment.Worksheet.CellBordersOrBackgroundChanged -= Worksheet_BordersChanged;
            if (newFragment != null)
                newFragment.Worksheet.CellBordersOrBackgroundChanged += Worksheet_BordersChanged;
            base.OnWorksheetPaneChanged(d, e);
            DisposeView();
        }

        private void DisposeView()
        {
            if (_view != null)
                _view.Dispose();
            _view = null;
        }

        private void Worksheet_BordersChanged(object sender, EventArgs e)
        {
            DisposeView();
            InvalidateSelf(null);
        }

        private void CellBordersControlUnloaded(object sender, RoutedEventArgs e)
        {
            DisposeView();
        }


        protected override void OnRender(DrawingContext drawingContext)
        {
            Theme theme = DrawingElement.GetTheme(this);
            ColorScheme colorScheme = null;
            if (theme != null)
                colorScheme = theme.ColorScheme;
            if (_previousScheme != colorScheme)
            {
                _previousScheme = colorScheme;
                _pens = new Dictionary<Pair<SpreadsheetColor, BorderStyle>, Pen>();
            }
            RenderRowColumnBorders(drawingContext, colorScheme);
            RenderCellBorders(drawingContext, colorScheme);
        }

        private void RenderCellBorders(DrawingContext drawingContext, ColorScheme colorScheme)
        {
            foreach (Cell cell in Enumerable.Where(GetCells(), c => c.Borders != null && c.IsVisible))
            {
                Borders borders = cell.ActualBorders;
#if DEBUG
                Contract.Assert((!cell.IsDeleted));
#endif
                Contract.Assert(borders != null);
                Rect rect = GetRelativeRect(cell);
                BorderDrawer.DrawBorders(drawingContext, borders, rect, colorScheme, getPen);
            }
        }

        protected override IEnumerable<Cell> GetCells()
        {
            if (WorksheetPane is WorksheetPane)
                return View.GetCells();
            return base.GetCells();
        }


        private void RenderRowColumnBorders(DrawingContext drawingContext, ColorScheme colorScheme)
        {
            if (WorksheetPane == null)
                return;
            foreach (
                Column column in
                    Enumerable.Where(WorksheetPane.GetColumns(),
                                     c => c.ActualWidth > 0 && c.IsVisible && c.Borders != null))
            {
                Borders borders = column.Borders;
                BorderDrawer.DrawBorders(drawingContext, borders, GetRect(column), colorScheme, getPen);
                if (borders.Bottom != null | borders.Top != null)
                {
                    foreach (Row row in WorksheetPane.GetRows())
                    {
                        Cell cell = Worksheet.GetNonVolatileCell(row.Index, column.Index);
                        if (cell != null)
                        {
                            if (cell.Borders == null)
                                BorderDrawer.DrawBorders(drawingContext, borders, GetRelativeRect(cell), colorScheme,
                                                         getPen);
                        }
                        else
                        {
                            BorderDrawer.DrawBorders(drawingContext, borders,
                                                     GetRect(new CellAddress(row.Index, column.Index)), colorScheme,
                                                     getPen);
                        }
                    }
                }
            }
            foreach (
                Row row in
                    Enumerable.Where(WorksheetPane.GetRows(),
                                     row => row.IsVisible && row.ActualHeight > 0 && row.Borders != null))
            {
                Borders borders = row.Borders;
                BorderDrawer.DrawBorders(drawingContext, borders, GetRect(row), colorScheme, getPen);
                if (borders.Left != null | borders.Right != null)
                {
                    foreach (Column column in WorksheetPane.GetColumns())
                    {
                        Cell cell = Worksheet.GetNonVolatileCell(row.Index, column.Index);
                        if (cell != null)
                        {
                            if (cell.Borders == null)
                                BorderDrawer.DrawBorders(drawingContext, borders, GetRelativeRect(cell), colorScheme,
                                                         getPen);
                        }
                        else
                        {
                            BorderDrawer.DrawBorders(drawingContext, borders,
                                                     GetRect(new CellAddress(row.Index, column.Index)), colorScheme,
                                                     getPen);
                        }
                    }
                }
            }
        }


        private Pen getPen(Border border, ColorScheme colorScheme)
        {
            Pen result;
            if (_pens.TryGetValue(new Pair<SpreadsheetColor, BorderStyle>(border.Color, border.Style), out result))
                return result;
            result = new Pen(new SolidColorBrush(border.GetWPFColor(colorScheme)),
                             BorderStyleToThicknessConverter.StyleToThickness(border.Style))
                         {
                             DashStyle =
                                 BorderStyleToDashConverter.StyleToDash(border.Style),
                             EndLineCap = PenLineCap.Square,
                             StartLineCap = PenLineCap.Square,
                             DashCap = PenLineCap.Flat
                         }; //\\ missing dash;
            if (result.CanFreeze)
                result.Freeze();
            _pens.Add(new Pair<SpreadsheetColor, BorderStyle>(border.Color, border.Style), result);
            return result;
        }


        protected override bool IsRenderProperty(string name)
        {
            switch (name)
            {
                case CellProperties.Width:
                case CellProperties.ActualWidth:
                case CellProperties.Height:
                case CellProperties.ActualHeight:
                case CellProperties.ActualBorders:
                case CellProperties.Borders:
                case CellProperties.IsVisible:
                case CellProperties.DifferentialStyleRecord:

                    return true;
                case CellProperties.ColorScale:
                case CellProperties.DataBar:
                case CellProperties.ActualFontWeight:
                case CellProperties.ActualFontStyle:
                case CellProperties.ActualFontUnderline:

                case CellProperties.Indentation:
                case CellProperties.CanOverflow:
                case CellProperties.FormattedValue:
                case CellProperties.FormulaFormattedValue:
                case CellProperties.Value:
                case CellProperties.FontUnderline:
                case CellProperties.FontFamily:
                case CellProperties.ActualFontSize:
                case CellProperties.FontSize:
                case CellProperties.Foreground:
                case CellProperties.ActualForeground:
                case CellProperties.Format:
                case CellProperties.VerticalAlignment:
                case CellProperties.HorizontalAlignment:
                case CellProperties.TextAlignment:
                case CellProperties.TextWrapping:
                case CellProperties.FontWeight:
                case CellProperties.FontStyle:
                case CellProperties.Background:
                case CellProperties.ActualBackground:
                case CellProperties.IsSelected:
                case CellProperties.Formula:
                case CellProperties.LocalizedFormula:
                case CellProperties.ArrayFormulaRange:
                case CellProperties.Row:
                case CellProperties.Column:
                case CellProperties.Index:
                case CellProperties.IsLocked:
                case CellProperties.Comment:
                case CellProperties.Offset:
                    return false;
            }
            Debug.WriteLine(string.Format("unhandled property {0} in {1}", name, GetType()));
            return false;
        }
    }
}